package com.cirnoworks.uhs;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;

import com.cirnoworks.common.ByteBufferUtil;

public class Protocal {
	private Protocal() {

	}

	public static void requestHeader(ByteBuffer buf, long seq, int op) {
		buf.put((byte) op);
		buf.putLong(seq);
	}

	public static void responseHeader(ByteBuffer buf, long seq, int op, int code) {
		buf.put((byte) op);
		buf.putLong(seq);
		buf.put((byte) code);
	}

	public static String toString(ByteBuffer buf) {
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		buf.mark();
		while (buf.hasRemaining()) {
			int b = buf.get() & 0xff;
			if (b < 16) {
				sb.append('0');
			}
			sb.append(Integer.toHexString(b));
			if (buf.hasRemaining()) {
				sb.append(",");
			}
		}
		buf.reset();
		sb.append("]");
		return sb.toString();
	}

	public static long getAddress(InetAddress address) {
		long ret = 0;
		byte[] addr = address.getAddress();
		for (int i = 0, max = addr.length; i < max; i++) {
			ret <<= 8;
			ret |= (addr[i]) & 0xff;
		}
		return ret;
	}

	public static InetAddress fromAddress(long address)
			throws UnknownHostException {
		byte[] addr;
		if ((0xFFffFFff00000000l & address) != 0) {
			addr = new byte[8];
		} else {
			addr = new byte[4];
		}

		for (int i = addr.length - 1; i >= 0; i--) {
			addr[i] = (byte) address;
			address >>>= 8;
		}

		return InetAddress.getByAddress(addr);
	}

	public static class CmdPing {
		public static final int OP = 0;
	}

	public static class CmdPortMap {
		public static final int OP = 2;

		public static void request(ByteBuffer buf, long seq, byte[] userKey,
				int port, int redirectorPort) {
			requestHeader(buf, seq, OP);
			buf.put(userKey);
			buf.putChar((char) port);
			buf.putChar((char) redirectorPort);
		}

		public static void response(ByteBuffer buf, long seq, int code) {
			responseHeader(buf, seq, OP, code);
		}

		public static String responseToString(ByteBuffer buf) {
			buf.mark();
			try {
				if (buf.remaining() < 1) {
					return null;
				}
				int op = buf.get();
				if (op != OP) {
					return "INVALID OP " + op;
				}
				int code = buf.get();
				if (code == 0) {
					return "OK";
				} else {
					return "ERR " + code + " " + buf.remaining();
				}
			} finally {
				buf.reset();
			}
		}
	}

	public static class CmdGetPortMap {
		public static final int OP = 1;

		public static void request(ByteBuffer buf, long seq, String userName,
				int port) {
			requestHeader(buf, seq, OP);
			ByteBufferUtil.putString(buf, userName);
			buf.putChar((char) port);
		}

		public static void response(ByteBuffer buf, long seq,
				SocketAddress address) {
			responseHeader(buf, seq, OP, 0);
			InetSocketAddress isa = (InetSocketAddress) address;
			InetAddress ia = isa.getAddress();
			long addr = 0;
			for (byte b : ia.getAddress()) {
				addr |= b & 0xff;
			}
			buf.putLong(addr);
			buf.putChar((char) isa.getPort());
		}

		public static String responseToString(ByteBuffer buf) {
			buf.mark();
			try {
				if (buf.remaining() < 1) {
					return null;
				}
				int op = buf.get();
				if (op != OP) {
					return "INVALID OP " + op;
				}
				int code = buf.get();
				if (code == 0) {
					return "OK";
				} else {
					return "ERR " + code + " " + buf.remaining();
				}
			} finally {
				buf.reset();
			}
		}
	}

	public static class RedirectPayload {
		public static final int OP = 3;
	}

}
